#ifndef _THESIS_CACHE_H_
#define _THESIS_CACHE_H_

#include <vector>
#include <memory>
#include <iostream>
#include <iomanip>
#include "lruList.h"

const int FACT_TABLE_TUPLE = 0;
const int HASH_TABLE_TUPLE = 1;

const int CACHE_LINE_SIZE = 64;
const int PAGE_SIZE = 4096 / CACHE_LINE_SIZE;
const int NUM_CACHE_ENTRY = 24;
const int NUM_CACHE_SET = 4096;
const int NUM_COLOR = NUM_CACHE_SET / PAGE_SIZE;
const int COLOR_SHIFT = 6;
const int COLOR_MASK = NUM_COLOR - 1;

inline int get_color_num(unsigned long addr){
	return (addr >> COLOR_SHIFT) & COLOR_MASK;
}

inline bool color_aligned(unsigned long addr){
	return (addr & ~PAGE_SIZE) == 0;
}

class cache {
public:
	typedef lru_list<unsigned long, int> cache_set_t;
	typedef std::shared_ptr<cache_set_t> cache_set_ptr;

	cache (unsigned int e, unsigned int s) : num_entry(e), num_set(s){
		sets.resize(s);
		for(unsigned int i = 0 ; i < s ; ++i){
			sets[i] = cache_set_ptr(new cache_set_t(e));
		}
	}

	~cache () {}

public:
	bool access(unsigned long addr, int value){
		bool retval;
		unsigned int set = addr % num_set;
		retval = sets[set]->find(addr, value);
		if(false == retval){
			sets[set]->insert(addr, value);
		}
		return retval;
	}
	
	void dump_each_color(){
		for(int color = 0 ; color < NUM_COLOR ; ++color){
			unsigned int num_fact_items = 0;
			unsigned int num_hash_items = 0;
			for(int i = color * NUM_COLOR ; i < color * NUM_COLOR + NUM_CACHE_SET / NUM_COLOR ; ++i){
				sets[i]->lock();
				cache_set_t::iterator start, end;
				start = sets[i]->begin();
				end = sets[i]->end();
				for(; start != end ; ++start){
					if(start->second == FACT_TABLE_TUPLE){
						++num_fact_items;
					}
					else if(start->second == HASH_TABLE_TUPLE){
						++num_hash_items;
					}
					else {
						assert(false);
					}
				}
				sets[i]->unlock();
			}
			std::cout << std::setprecision(2) << num_hash_items / (double)(num_hash_items + num_fact_items) * 100 << "%\t";
		}
		std::cout << std::endl;
	}

	void dump_each_lru_list(){
		for(unsigned int i = 0 ; i < num_set ; ++i){
			unsigned int num_fact_items = 0;
			unsigned int num_hash_items = 0;
			sets[i]->lock();
			cache_set_t::iterator start, end;
			start = sets[i]->begin();
			end = sets[i]->end();
			for(; start != end ; ++start){
				if(start->second == FACT_TABLE_TUPLE){
					++num_fact_items;
				}
				else if(start->second == HASH_TABLE_TUPLE){
					++num_hash_items;
				}
				else {
					assert(false);
				}
			}
			sets[i]->unlock();
			std::cout << std::setprecision(2) << num_hash_items / (double)(num_hash_items + num_fact_items) * 100 << "%\t";
		}
		std::cout << std::endl;
	}

	void dump (){
		unsigned int num_fact_items = 0;
		unsigned int num_hash_items = 0;
		for(unsigned int i = 0 ; i < num_set ; ++i){
			sets[i]->lock();
			cache_set_t::iterator start, end;
			start = sets[i]->begin();
			end = sets[i]->end();
			for(; start != end ; ++start){
				if(start->second == FACT_TABLE_TUPLE){
					++num_fact_items;
				}
				else if(start->second == HASH_TABLE_TUPLE){
					++num_hash_items;
				}
				else {
					assert(false);
				}
			}
			sets[i]->unlock();
		}
		std::cout << "fact items: " <<num_fact_items << " hash items: " << num_hash_items << " hash items rate: " 
			<< std::setprecision(2) << num_hash_items / (double)(num_hash_items + num_fact_items) * 100<< "%\n";
	}

private:
	unsigned int num_set;
	unsigned int num_entry;
	std::vector<cache_set_ptr> sets;
};

#endif /* _THESIS_CACHE_H_ */